#ifndef cope_h
#define cope_h


#include "cope-timer.h"
#include "cope-packet.h"
#include "cope-queue.h"

#include "mobilenode.h"
#include "ackqueue.h"
#include "packet-info.h"
#include "packet-pool.h"
#include "prob-guess.h"
#include "mac-802_11.h"
#include "mac.h"

#include "uthash.h"
#include "config.h"
#include "lib/bsd-list.h"
#include <fstream>
//using namespace std;
#include "string"
using std::string;
/* 
 * estimate retransmit time period,
 * how many times of mac layer's tramsmission time
 */

/* ======================================================================
                             COPENeighbor
			     ============
                 Class used to construct a neighbor list
   ====================================================================== */

class COPENeighbor {
  friend class COPE;
  friend class ProbGuess;
 public:
  COPENeighbor(int id, int value = 0) { 
    nb_node_id_ = id; 
    }
  inline int getid(){return nb_node_id_;}
 protected:
  int nb_node_id_;
 private:
  LIST_ENTRY(COPENeighbor) nb_link;
};
LIST_HEAD(nb_head, COPENeighbor);

/* ======================================================================
                           COPENeighborPkt
			   ===============
         Class inherit from COPENeighbor, used to construct a 
         neighbor-packet pair list
   ====================================================================== */

class COPENeighborPkt : public COPENeighbor{
    friend class COPE;
 public:
 COPENeighborPkt(int id, Packet * pkt, double prob = 1.0 ) : COPENeighbor(id) {
	encode_pkt_ = pkt;
	encode_pkt_id_ = HDR_CMN(pkt)->cope_pkt_id();
	encode_prob_ = prob;
  }
    inline int get_pkt_id(){return encode_pkt_id_;}
    inline Packet * get_pkt(){return encode_pkt_;}
    inline void update_encode_prob(double prob) { encode_prob_ = prob;}
    inline double get_encode_prob() {return encode_prob_;}
 protected:
    Packet * encode_pkt_;
    int encode_pkt_id_;
    double encode_prob_;
 private:
    LIST_ENTRY(COPENeighborPkt) nb_link;
};
LIST_HEAD(nb_pkt_head, COPENeighborPkt);

/* ======================================================================
                           COPENeighborCounter
                           ===================
         Class inherited from COPENeighbor, used to record the
	 local sequence counter for each neighbor
   ====================================================================== */

class COPELocalCounter : public COPENeighbor {
    friend class COPE;
 public:
    /* default is 0 */
 COPELocalCounter(int nodeid, u_int16_t local_seqnum = 0) : COPENeighbor(nodeid) {
	local_seq_counter_ = local_seqnum;
    }
    inline u_int16_t local_seq_counter() { return local_seq_counter_; }
 protected:
    u_int16_t local_seq_counter_;
    // private:
 public:
    LIST_ENTRY(COPELocalCounter) nb_link;
};

LIST_HEAD(local_counter_head, COPELocalCounter);


/* ======================================================================
   Macro define, used as op assistants for COPENeighbor family classes
   ====================================================================== */

#define COPE_ND_INSERT(type, head, id, value) {				\
  type * nb = new type(id, value);				       \
  assert(nb);							       \
  LIST_INSERT_HEAD(head, nb, nb_link);				       \
  }

#define COPE_ND_LOOKUP(type, head, id, rtval) {			       \
	type * nb = (head).lh_first;				       \
	for(; nb; nb = (nb->nb_link).le_next) {			       \
	    if(nb->getid() == id)				       \
		break;						       \
	}							       \
	rtval = nb;						       \
}                                                                      

#define COPE_GET_HEAD(head, ret) {				       \
	if((ret = (head)->lh_first) == NULL)			       \
	    ret = NULL;						       \
    }                                             

#define COPE_GET_NEXT(cur, ret) {                                      \
	if((ret = (cur)->nb_link.le_next) == NULL)		       \
	    ret = NULL;						       \
  }                                             

#define COPE_ND_DELETE(type, head, id) {			       \
	type * nb = (head).lh_first;				       \
	for(; nb; nb = nb->nb_link.le_next) {			       \
	    if(nb->getid() == id) {				       \
		LIST_REMOVE(nb,nb_link);			       \
		delete nb;					       \
		break;						       \
	    }							       \
	}							       \
    }

#define COPE_ND_CLEAR(type, head) {				       \
        type * nb = (head)->lh_first;				       \
	for(; nb; nb = nb->nb_link.le_next) {			       \
	  LIST_REMOVE(nb, nb_link);				       \
	  delete nb;						       \
	}							       \
    }

struct local_counter_head;


/* ======================================================================
                            COPEHandler
                            ===========
                    Handles callbacks for COPE
   ====================================================================== */
class COPEHandler : public Handler {
 public:
    inline COPEHandler(COPE * cope_inst) : cope_inst_(cope_inst) {
    }
    void handle(Event*);
 private: 
    COPE * cope_inst_;
};

class AckPendHandler : public Handler {
 public:
    inline AckPendHandler(COPE* cope_inst) : cope_inst_(cope_inst) {
    }
    void handle(Event* e);
    //    int neighbor_;
 private:
    COPE * cope_inst_;
};

class PacketPoolHandler : public Handler {
 public:
    inline PacketPoolHandler(COPE* cope_inst) : cope_inst_(cope_inst) {
    }
    void handle(Event* e);
    void start();
 private:
    bool first_;
    COPE * cope_inst_;    
};


/* ======================================================================
                                   COPE
				   ====
        Main logical algorithm for COPE, with all queues encapsulated 
	inside, used as a IFQ proxy by all nodes 
   ====================================================================== */

class COPEPriQueue;

class COPE : public Tap, public NsObject {

 public:
     COPE();
     ~COPE();

 public:
     friend class COPEPriQueue;
     friend class AckPendHandler;
     friend class PacketPoolHandler;

     /***********Public Functions ************/

     int command(int argc, const char*const* argv);
     
     /*
      * Tap out all data packets (both encoded and native packets) received at
      * this host and promiscuously snoop them for interesting tidbits.
      * Tap is given all packets received by the host.
      * it must not alter or free the pkt.  If you want to frob it, copy it.
      */
     void tap(const Packet* p);     
     
     /*
      * resume the dequeue and encoding process, is called by COPEHandler
      */
     void cope_resume();

     /*
      * Judge the packet direction, then send it down or up
      */
     void recv(Packet * /* p */, Handler * /* h */); 

     /*
      * the main encode process: assemble maximal native packets as possible  
      */
     int encode(Packet * /* p */, Packet** /* res_pkt */, Packet** /* max_size_pkt */);

     /*
      * Handler of the NonAckTimer timeout 
      */
     void non_ack_handler(Event* e);
     
     /*
      * Start timer for all the native packets in the encode packet
      */
     void start_timer(Packet *p, double timeout);

     void stop_timer(int nodeid, u_int16_t local);
     void stop_timer(Packet* p);
     
     /*
      * Control Packet Handler
      */
     void ctrl_pkt_handler(Handler *);

     double txtime(Packet * );

     string convBase(unsigned long v, long base);
 protected:
     /*********Protected Functions***************/
     void initNeighborCounters(local_counter_head * );
     void getNeighborList( nb_head * );

     /*
      * assemble the cope header for different field
      */
     void append_xored_pkt(Packet * , hdr_xored_t , Packet *, Packet**);
     void append_state_report(Packet *); 
     void append_acks(Packet * );

     /*
      * Set the two variables in Packet class:
      * int cope_pkt_id_ and u_int16_t cope_seq_num_.
      */
     void set_pkt_header(Packet* /* p */);

     /*
      * Main decode process: decode the packet, get the interested native packet
      * and put it into packet pool, if not decodable, the encoded packet
      * will also be put into packet pool, for a further decoding possibility. 
      * 
      * NOTE that although we just simulate an xor operation, we assume xor will
      * also operate on all headers except for COPE header, so these headers 
      * could be recovered after decoding, and COPE header's xor field will
      * be deleted
      */
     int decode(Packet**);
   
     /* 
      * decode, extract state report and extract acks, judge whether the packet 
      * is reveivable or not, only native packet and decoded packet are regarded
      * as reveivable packet which can be sent up
      */
     int receivable(Packet**);

     /*
      * Extract the packet headers, and update corresponding information,
      * state report update packet info, acks update NonAckQueues
      */
     void extract_state_report(Packet *);
     void extract_acks(Packet *);
  
     /*
      * internal functions called by encode member function
      */
     int encodable(char /*large or small virqueue*/, 
		   int /*vir_nb_id*/ ,
		   nb_pkt_head * /* the next hops set */);
     void traverse_remained_nbs(char  /*large or small virqueue*/, 
				nb_pkt_head * /*the next hops set*/,
				nb_head * /* remain_nb_list */
				);
     
     // retransmission
     void retrasmit(int nodeid, u_int16_t local);
 
 
     /***********Protected Variables***********/
     
     COPEPriQueue * cope_prq_;

     /*
      * Each node has a Non-Ack-Queue to store all native packet sent out,
      * and schedule restransmission for each packet.
      */    
     NonAckQueue* non_acks_;   
     AckPendQueue* pend_acks_;   
  
     PacketPool* pkt_pool_;
     PacketInfo* pkt_info_;
     ProbGuess* prob_guess_;

     //     MobileNode* node_;
     Mac802_11 * mac_;

     /* Local sequence number counter for each neighbor */
     local_counter_head local_counter_list_;

     /* Neighbor List */
     nb_head neighbor_list_;

     /*
      * Used as a restransmission timer, start it for each packet in the
      * encoded packet when sending out.
      */
     list<NonAckTimer*>* non_ack_timers_;

     /*
      * Used as a control packet timer, once no packet sent out in a specific
      * period, the control packet would be sent out instead.
      */
     CtrlPktTimer ctrl_pkt_timer_;

     // record the total number of packets sent out by this node, cope_seq_num_
     u_int16_t cope_index_;

     // the factor of trasimit time
     int txtime_factor_;

 public:
     COPEHandler h_cope_;
     
     Event pend_event_;
     AckPendHandler h_ack_pend_;

     double gc_interval_; // garbage collect interval of packet pool
     Event pool_event_;     
     PacketPoolHandler h_pkt_pool_;
   
     ofstream qout;
     ofstream eout;
     MobileNode* node_;

};

#endif
